Cache cursors to avoid libXcursor theme lookup overhead.
authorMatthias Clasen <matthiasc@src.gnome.org>
Tue, 20 Jan 2009 02:15:59 +0000 (02:15 +0000)
committerMatthias Clasen <matthiasc@src.gnome.org>
Tue, 20 Jan 2009 02:15:59 +0000 (02:15 +0000)
        Patch by David Alan Gilbert.

        * gdk/gdkcursor.h: Add a GDK_BLANK_CURSOR cursor type.

        * gdk/x11/gdkcursor-x11.c: Cache font cursors and named cursors.

        * gdk/x11/gdkprivate-x11.h:
        * gdk/x11/gdkcdisplay-x11.c: Remove cached cursors when a
        display if finalized.

svn path=/trunk/; revision=22145

ChangeLog
gdk/gdkcursor.h
gdk/x11/gdkcursor-x11.c
gdk/x11/gdkdisplay-x11.c
gdk/x11/gdkprivate-x11.h

index e56dd8911bf8598b9cc00f5d964132a0e98416bd..503535ebd51a5ed6a2d61ea296c57ba91ce2f9e3 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,16 @@
+2009-01-19  Matthias Clasen  <mclasen@redhat.com>
+
+       Cache cursors to avoid libXcursor theme lookup overhead.
+       Patch by David Alan Gilbert.
+
+       * gdk/gdkcursor.h: Add a GDK_BLANK_CURSOR cursor type.
+
+       * gdk/x11/gdkcursor-x11.c: Cache font cursors and named cursors.
+
+       * gdk/x11/gdkprivate-x11.h:
+       * gdk/x11/gdkcdisplay-x11.c: Remove cached cursors when a 
+       display if finalized.
+
 2009-01-18  Matthias Clasen  <mclasen@redhat.com>
 
        Bug 568263 – gtk can't recognize the wrong X Selection TARGETS' 
index 488faa498f3711dcd288d418b91373a4fd46d154..f20400368dd6c95ca9e2e091f40657d7517d6b76 100644 (file)
@@ -120,7 +120,8 @@ typedef enum
   GDK_WATCH              = 150,
   GDK_XTERM              = 152,
   GDK_LAST_CURSOR,
-  GDK_CURSOR_IS_PIXMAP         = -1
+  GDK_BLANK_CURSOR        = -2,
+  GDK_CURSOR_IS_PIXMAP           = -1
 } GdkCursorType;
 
 struct _GdkCursor
index ef18c519dc44ddaa0876f7317a459540c79d8c32..d40f6ddc6c66187aa1ab9c9c76f4205ea7294842 100644 (file)
 
 static guint theme_serial = 0;
 
+/* cursor_cache holds a cache of non-pixmap cursors to avoid expensive 
+ * libXcursor searches, cursors are added to it but never removed. We make 
+ * the assumption that since there are a small number of GdkDisplay's and 
+ * a small number of cursor's that this list will stay small enough
+ * not to be a problem.
+ */
+static GSList* cursor_cache = NULL;
+
+struct cursor_cache_key
+{
+  GdkDisplay* display;
+  GdkCursorType type;
+  const char* name;
+};
+
+/* Caller should check if there is already a match first.
+ * Cursor MUST be either a typed cursor or a pixmap with 
+ * a non-NULL name.
+ */
+static void
+add_to_cache (GdkCursorPrivate* cursor)
+{
+  cursor_cache = g_slist_prepend (cursor_cache, cursor);
+
+  /* Take a ref so that if the caller frees it we still have it */
+  gdk_cursor_ref ((GdkCursor*) cursor);
+}
+
+/* Returns 0 on a match
+ */
+static gint
+cache_compare_func (gconstpointer listelem, 
+                    gconstpointer target)
+{
+  GdkCursorPrivate* cursor = (GdkCursorPrivate*)listelem;
+  struct cursor_cache_key* key = (struct cursor_cache_key*)target;
+
+  if ((cursor->cursor.type != key->type) ||
+      (cursor->display != key->display))
+    return 1; /* No match */
+  
+  /* Elements marked as pixmap must be named cursors 
+   * (since we don't store normal pixmap cursors 
+   */
+  if (key->type == GDK_CURSOR_IS_PIXMAP)
+    return strcmp (key->name, cursor->name);
+
+  return 0; /* Match */
+}
+
+/* Returns the cursor if there is a match, NULL if not
+ * For named cursors type shall be GDK_CURSOR_IS_PIXMAP
+ * For unnamed, typed cursors, name shall be NULL
+ */
+static GdkCursorPrivate*
+find_in_cache (GdkDisplay    *display, 
+               GdkCursorType  type,
+               const char    *name)
+{
+  GSList* res;
+  struct cursor_cache_key key;
+
+  key.display = display;
+  key.type = type;
+  key.name = name;
+
+  res = g_slist_find_custom (cursor_cache, &key, cache_compare_func);
+
+  if (res)
+    return (GdkCursorPrivate *) res->data;
+
+  return NULL;
+}
+
+/* Called by gdk_display_x11_finalize to flush any cached cursors
+ * for a dead display.
+ */
+void 
+_gdk_x11_cursor_display_finalize (GdkDisplay *display)
+{
+  GSList* item;
+  GSList** itemp; /* Pointer to the thing to fix when we delete an item */
+  item = cursor_cache;
+  itemp = &cursor_cache;
+  while (item)
+    {
+      GdkCursorPrivate* cursor = (GdkCursorPrivate*)(item->data);
+      if (cursor->display == display)
+        {
+         GSList* olditem;
+          gdk_cursor_unref ((GdkCursor*) cursor);
+         /* Remove this item from the list */
+         *(itemp) = item->next;
+         olditem = item;
+         item = g_slist_next (item);
+         g_slist_free_1 (olditem);
+        } 
+      else 
+        {
+         itemp = &(item->next);
+         item = g_slist_next (item);
+       }
+    }
+}
+
+static Cursor
+get_blank_cursor (GdkDisplay *display)
+{
+  GdkScreen *screen;
+  GdkPixmap *pixmap;
+  Pixmap source_pixmap;
+  XColor color;
+  Cursor cursor;
+
+  screen = gdk_display_get_default_screen (display);
+  pixmap = gdk_bitmap_create_from_data (gdk_screen_get_root_window (screen), 
+                                       "\0\0\0\0\0\0\0\0", 1, 1);
+  source_pixmap = GDK_PIXMAP_XID (pixmap);
+
+  color.pixel = 0; 
+  color.red = color.blue = color.green = 0;
+  
+  if (display->closed)
+    cursor = None;
+  else
+    cursor = XCreatePixmapCursor (GDK_DISPLAY_XDISPLAY (display),
+                                  source_pixmap, source_pixmap,
+                                  &color, &color, 1, 1);
+  g_object_unref (pixmap);
+
+  return cursor;
+}
+
 /**
  * gdk_cursor_new_for_display:
  * @display: the #GdkDisplay for which the cursor will be created
@@ -108,11 +242,11 @@ static guint theme_serial = 0;
  * <listitem><para>
  * <inlinegraphic format="PNG" fileref="sb_v_double_arrow.png"></inlinegraphic> #GDK_SB_V_DOUBLE_ARROW (move horizontal splitter)
  * </para></listitem>
+ * <listitem><para>
+ * #GDK_BLANK_CURSOR (Blank cursor). Since 2.16
+ * </para></listitem>
  * </itemizedlist>
  *
- * To make the cursor invisible, use gdk_cursor_new_from_pixmap() to create
- * a cursor with no pixels in it.
- * 
  * Return value: a new #GdkCursor
  *
  * Since: 2.2
@@ -128,9 +262,29 @@ gdk_cursor_new_for_display (GdkDisplay    *display,
   g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
 
   if (display->closed)
-    xcursor = None;
-  else
-    xcursor = XCreateFontCursor (GDK_DISPLAY_XDISPLAY (display), cursor_type);
+    {
+      xcursor = None;
+    } 
+  else 
+    {
+      private = find_in_cache (display, cursor_type, NULL);
+
+      if (private)
+        {
+          /* Cache had it, add a ref for this user */
+          gdk_cursor_ref ((GdkCursor*) private);
+       
+          return (GdkCursor*) private;
+        } 
+      else 
+        {
+         if (cursor_type != GDK_BLANK_CURSOR)
+            xcursor = XCreateFontCursor (GDK_DISPLAY_XDISPLAY (display),
+                                         cursor_type);
+         else
+           xcursor = get_blank_cursor (display);
+       }
+    }
   
   private = g_new (GdkCursorPrivate, 1);
   private->display = display;
@@ -142,6 +296,9 @@ gdk_cursor_new_for_display (GdkDisplay    *display,
   cursor->type = cursor_type;
   cursor->ref_count = 1;
   
+  if (xcursor != None)
+    add_to_cache (private);
+
   return cursor;
 }
 
@@ -427,25 +584,20 @@ _gdk_x11_cursor_update_theme (GdkCursor *cursor)
        new_cursor = XcursorShapeLoadCursor (xdisplay, cursor->type);
       
       if (new_cursor != None)
-       XFixesChangeCursor (xdisplay, new_cursor, private->xcursor);
+       {
+         XFixesChangeCursor (xdisplay, new_cursor, private->xcursor);
+         private->xcursor = new_cursor;
+       }
     }
 }
 
 static void
-update_cursor (gpointer key,
-              gpointer value,
-              gpointer data)
+update_cursor (gpointer data,
+              gpointer user_data)
 {
-  XID *xid = key;
   GdkCursor *cursor;
 
-  if (*xid & XID_FONT_BIT)
-    return;
-
-  if (!GDK_IS_WINDOW (value))
-    return;
-
-  cursor = _gdk_x11_window_get_cursor (GDK_WINDOW (value));
+  cursor = (GdkCursor*)(data);
 
   if (!cursor)
     return;
@@ -503,7 +655,7 @@ gdk_x11_display_set_cursor_theme (GdkDisplay  *display,
   if (size > 0)
     XcursorSetDefaultSize (xdisplay, size);
     
-  g_hash_table_foreach (display_x11->xid_ht, update_cursor, NULL);
+  g_slist_foreach (cursor_cache, update_cursor, NULL);
 }
 
 #else
@@ -679,6 +831,16 @@ gdk_cursor_new_from_name (GdkDisplay  *display,
     xcursor = None;
   else 
     {
+      private = find_in_cache (display, GDK_CURSOR_IS_PIXMAP, name);
+
+      if (private)
+        {
+          /* Cache had it, add a ref for this user */
+          gdk_cursor_ref ((GdkCursor*) private);
+
+          return (GdkCursor*) private;
+        }
+
       xdisplay = GDK_DISPLAY_XDISPLAY (display);
       xcursor = XcursorLibraryLoadCursor (xdisplay, name);
       if (xcursor == None)
@@ -694,7 +856,8 @@ gdk_cursor_new_from_name (GdkDisplay  *display,
   cursor = (GdkCursor *) private;
   cursor->type = GDK_CURSOR_IS_PIXMAP;
   cursor->ref_count = 1;
-  
+  add_to_cache (private);
+
   return cursor;
 }
 
index 87a5200498d0fab79ddfc3949ab0b3ade3d12ead..cdab44491006a641e033064e893bb674c4b91fc4 100644 (file)
@@ -838,6 +838,8 @@ gdk_display_x11_finalize (GObject *object)
       g_free (display_x11->motif_target_lists);
     }
 
+  _gdk_x11_cursor_display_finalize (GDK_DISPLAY_OBJECT(display_x11));
+
   /* Atom Hashtable */
   g_hash_table_destroy (display_x11->atom_from_virtual);
   g_hash_table_destroy (display_x11->atom_to_virtual);
index 1ae97c16231d02111a753df5ba9eab0999d704dc..f24a0df4c1adec038c04f4cf88e298ac0720823d 100644 (file)
@@ -191,6 +191,7 @@ PangoRenderer *_gdk_x11_renderer_get (GdkDrawable *drawable,
                                      GdkGC       *gc);
 
 void _gdk_x11_cursor_update_theme (GdkCursor *cursor);
+void _gdk_x11_cursor_display_finalize (GdkDisplay *display);
 
 gboolean _gdk_x11_get_xft_setting (GdkScreen   *screen,
                                   const gchar *name,